#include <QtGui>

#include "calculator.h"
#include "gpadeclarations.h"


#include <iostream>


#define CALC_MAX_DIGITS 16




Calculator::Calculator(QWidget *parent) : QDialog(parent),
                                          calc(new Ui::Calculator)
{
/*Necessary since I am using Ui.*/
  calc->setupUi(this);

/*Connect the number buttons to the appropriate slots.*/
  connect(calc->button0, SIGNAL(clicked()), this, SLOT(clicked0()));
  connect(calc->button1, SIGNAL(clicked()), this, SLOT(clicked1()));
  connect(calc->button2, SIGNAL(clicked()), this, SLOT(clicked2()));
  connect(calc->button3, SIGNAL(clicked()), this, SLOT(clicked3()));
  connect(calc->button4, SIGNAL(clicked()), this, SLOT(clicked4()));
  connect(calc->button5, SIGNAL(clicked()), this, SLOT(clicked5()));
  connect(calc->button6, SIGNAL(clicked()), this, SLOT(clicked6()));
  connect(calc->button7, SIGNAL(clicked()), this, SLOT(clicked7()));
  connect(calc->button8, SIGNAL(clicked()), this, SLOT(clicked8()));
  connect(calc->button9, SIGNAL(clicked()), this, SLOT(clicked9()));
  connect(calc->buttonDivide, SIGNAL(clicked()), this,
          SLOT(clickedDivide()));
  connect(calc->buttonEqual, SIGNAL(clicked()), this, SLOT(clickedEqual()));
  connect(calc->buttonAdd, SIGNAL(clicked()), this, SLOT(clickedAdd()));
  connect(calc->buttonSubtract, SIGNAL(clicked()), this,
          SLOT(clickedSubtract()));
  connect(calc->buttonMultiply, SIGNAL(clicked()), this,
          SLOT(clickedMultiply()));
  connect(calc->buttonPeriod, SIGNAL(clicked()), this, SLOT(clickedPeriod()));
  connect(calc->buttonMemStore, SIGNAL(clicked()), this,
          SLOT(clickedMemStore()));
  connect(calc->buttonMemClear, SIGNAL(clicked()), this,
          SLOT(clickedMemClear()));
  connect(calc->buttonMemRecall, SIGNAL(clicked()), this,
          SLOT(clickedMemRecall()));
  connect(calc->buttonClear, SIGNAL(clicked()), this, SLOT(clickedClear()));
  connect(calc->buttonBackspace, SIGNAL(clicked()), this,
          SLOT(clickedBackspace()));
  connect(calc->buttonNegate, SIGNAL(clicked()), this, SLOT(clickedNegate()));
    
  connect(calc->calculateGpaButton, SIGNAL(clicked()), this,
          SLOT(calculateGpa()));
    
/*Set the current operation to nothing.*/
  operation_=OPERATION_NOTHING;

/*Initialize the main window to 0*/
  calc->displayWindow->setText("0");
 
  calc->displayWindow->setAlignment(Qt::AlignRight);

  setPeriod_=false;
  
  
  /*Test*/
/*  for(int i=0; i<100; i++)
  {
    if(calc->gpaTable->item(i, 0)!=NULL)
      cout << calc->gpaTable->item(i, 0)->text().toStdString() << endl;
    if(calc->gpaTable->item(i, 1)!=NULL)
      cout << calc->gpaTable->item(i, 1)->text().toStdString() << endl;
    if(calc->gpaTable->item(i, 2)!=NULL)
      cout << calc->gpaTable->item(i, 2)->text().toStdString() << endl;
    
  }
  */
}



void Calculator :: clicked0()
{
  eraseLeadingZeroes();
  if(setPeriod_)
  {
    setPeriod_=false;
    calc->displayWindow->setText(calc->displayWindow->text()+".0");
    return;
  }
  if(numOfDigits()<CALC_MAX_DIGITS)
    if(calc->displayWindow->text().length()>0)
      calc->displayWindow->setText(calc->displayWindow->text()+'0'); 
}

void Calculator :: clicked1()
{
  eraseLeadingZeroes();
  if(setPeriod_)
  {
    setPeriod_=false;
    calc->displayWindow->setText(calc->displayWindow->text()+".1");
    return;
  }
  if(numOfDigits()<CALC_MAX_DIGITS)
    calc->displayWindow->setText(calc->displayWindow->text()+'1');
}

void Calculator :: clicked2()
{
  eraseLeadingZeroes();
  if(setPeriod_)
  {
    setPeriod_=false;
    calc->displayWindow->setText(calc->displayWindow->text()+".2");
    return;
  }
  if(numOfDigits()<CALC_MAX_DIGITS)
    calc->displayWindow->setText(calc->displayWindow->text()+'2');
}

void Calculator :: clicked3()
{
  eraseLeadingZeroes();
  if(setPeriod_)
  {
    setPeriod_=false;
    calc->displayWindow->setText(calc->displayWindow->text()+".3");
    return;
  }
  if(numOfDigits()<CALC_MAX_DIGITS)
    calc->displayWindow->setText(calc->displayWindow->text()+'3');
}

void Calculator :: clicked4()
{
  eraseLeadingZeroes();
  if(setPeriod_)
  {
    setPeriod_=false;
    calc->displayWindow->setText(calc->displayWindow->text()+".4");
    return;
  }
  if(numOfDigits()<CALC_MAX_DIGITS)
    calc->displayWindow->setText(calc->displayWindow->text()+'4');
}

void Calculator :: clicked5()
{
  eraseLeadingZeroes();
  if(setPeriod_)
  {
    setPeriod_=false;
    calc->displayWindow->setText(calc->displayWindow->text()+".5");
    return;
  }
  if(numOfDigits()<CALC_MAX_DIGITS)
    calc->displayWindow->setText(calc->displayWindow->text()+'5');
}

void Calculator :: clicked6()
{
  eraseLeadingZeroes();
  if(setPeriod_)
  {
    setPeriod_=false;
    calc->displayWindow->setText(calc->displayWindow->text()+".6");
    return;
  }
  if(numOfDigits()<CALC_MAX_DIGITS)
    calc->displayWindow->setText(calc->displayWindow->text()+'6');
}

void Calculator :: clicked7()
{
  eraseLeadingZeroes();
  if(setPeriod_)
  {
    setPeriod_=false;
    calc->displayWindow->setText(calc->displayWindow->text()+".7");
    return;
  }
  if(numOfDigits()<CALC_MAX_DIGITS)
    calc->displayWindow->setText(calc->displayWindow->text()+'7');
}

void Calculator :: clicked8()
{
  eraseLeadingZeroes();
  if(setPeriod_)
  {
    setPeriod_=false;
    calc->displayWindow->setText(calc->displayWindow->text()+".8");
    return;
  }
  if(numOfDigits()<CALC_MAX_DIGITS)
    calc->displayWindow->setText(calc->displayWindow->text()+'8');
}

void Calculator :: clicked9()
{
  eraseLeadingZeroes();
  if(setPeriod_)
  {
    setPeriod_=false;
    calc->displayWindow->setText(calc->displayWindow->text()+".9");
    return;
  }
  if(numOfDigits()<CALC_MAX_DIGITS)
    calc->displayWindow->setText(calc->displayWindow->text()+'9');
}

void Calculator :: clickedPeriod()
{
  if(!contains('.') && numOfDigits()<CALC_MAX_DIGITS)
  {
    setPeriod_=true;
  }
}


void Calculator :: clickedAdd()
{
  clickedEqual();
  operation_=OPERATION_ADD;
  calc->displayWindow->setText("0");
}

void Calculator :: clickedSubtract()
{
  clickedEqual();
  operation_=OPERATION_SUBTRACT;
  calc->displayWindow->setText("0");
}
    
void Calculator :: clickedMultiply()
{
  clickedEqual();
  operation_=OPERATION_MULTIPLY;
  calc->displayWindow->setText("0");
}
    
void Calculator :: clickedDivide()
{
  clickedEqual();
  operation_=OPERATION_DIVIDE;
  calc->displayWindow->setText("0");
}


void Calculator :: clickedEqual()
{
  if(operation_==OPERATION_NOTHING)
  {
    if(contains('.'))
    {
      result_.setDouble(calc->displayWindow->text().toDouble());
      number1_.setDouble(calc->displayWindow->text().toDouble());
    }
    else
    {
      result_.setLongLong(calc->displayWindow->text().toLongLong());
      number1_.setLongLong(calc->displayWindow->text().toLongLong());
    }
  }
  else if(operation_==OPERATION_ADD)
  {
    if(contains('.'))
    {
       number2_.setDouble(calc->displayWindow->text().toDouble());
    }
    else
    {
      number2_.setLongLong(calc->displayWindow->text().toLongLong());
    }
    result_.add(&number1_, &number2_);
    number1_=result_;
    calc->displayWindow->setText(number1_.getData().c_str());
    operation_=OPERATION_NOTHING;
  }
  else if(operation_==OPERATION_SUBTRACT)
  {
    if(contains('.'))
    {
       number2_.setDouble(calc->displayWindow->text().toDouble());
    }
    else
    {
      number2_.setLongLong(calc->displayWindow->text().toLongLong());
    }
    result_.sub(&number1_, &number2_);
    number1_=result_;
    calc->displayWindow->setText(number1_.getData().c_str());
    operation_=OPERATION_NOTHING;
  }
  else if(operation_==OPERATION_MULTIPLY)
  {
    if(contains('.'))
    {
       number2_.setDouble(calc->displayWindow->text().toDouble());
    }
    else
    {
      number2_.setLongLong(calc->displayWindow->text().toLongLong());
    }
    result_.mul(&number1_, &number2_);
    number1_=result_;
    calc->displayWindow->setText(number1_.getData().c_str());
    operation_=OPERATION_NOTHING;
  }
  else if(operation_==OPERATION_DIVIDE)
  {
    if(contains('.'))
    {
       number2_.setDouble(calc->displayWindow->text().toDouble());
    }
    else
    {
      number2_.setLongLong(calc->displayWindow->text().toLongLong());
    }
    result_.div(&number1_, &number2_);
    number1_=result_;
    calc->displayWindow->setText(number1_.getData().c_str());
    operation_=OPERATION_NOTHING;
  }
  else
  {
    /*Should never hit here, but good practice to output error anyway.*/
    std::cout << "Invalid operation error. Returning without executing.\n";
    return;
  } 
}



void Calculator :: clickedMemStore()
{
  memory_=calc->displayWindow->text();
}
    
void Calculator :: clickedMemClear()
{
  memory_.clear();
}

void Calculator :: clickedMemRecall()
{
  if(0==memory_.length())
  {
    memory_="0";
  }
  calc->displayWindow->setText(memory_);
}

void Calculator :: clickedClear()
{
  calc->displayWindow->setText("0");
  operation_=OPERATION_NOTHING;
  number1_.clearData();
  number2_.clearData();
  result_.clearData();
  setPeriod_=false;
}


void Calculator :: clickedBackspace()
{
  string tmp=calc->displayWindow->text().toStdString();
  if(tmp.length()<1)
    return;  
  tmp.erase(tmp.length()-1, 1);
/*If the last character is a period, erase it.*/
  if('.'==tmp[tmp.length()-1])
    tmp.erase(tmp.length()-1, 1);
  calc->displayWindow->setText(tmp.c_str());
}



void Calculator :: clickedNegate()
{
  if(calc->displayWindow->text().toStdString()[0]=='-')
  {
    string temporary=calc->displayWindow->text().toStdString();
    temporary.erase(0, 1);
    calc->displayWindow->setText(temporary.c_str());

  }
  else
    calc->displayWindow->setText("-"+calc->displayWindow->text());
}



void Calculator :: eraseLeadingZeroes()
{
  string displayWindowText=calc->displayWindow->text().toStdString();
  while(displayWindowText[0]=='0')
  {
    displayWindowText.erase(0,1);

  }
  calc->displayWindow->setText(displayWindowText.c_str());
}

bool Calculator :: contains(char x)
{
/*Returns true if character x is found in the string, false otherwise.*/
  if(string::npos!=calc->displayWindow->text().toStdString().find(x))
    return true;
  return false;
}


int Calculator :: numOfDigits()
{
  int lengthModifier=0;
  if(contains('.'))
    lengthModifier++;
  if(contains('-'))
    lengthModifier++;
  return calc->displayWindow->text().length()-lengthModifier;
}


void Calculator :: calculateGpa()
{
  int i, j;
  long double accumulator;
  float numberOfCredits;
  string cell1, cell2;
  stringstream output;
  float input1, input2;
  char tmpGrade[2];
  double result;
  
  QString tmp;
  
  
  accumulator=0.0;
  numberOfCredits=0.0;
  
  for(i=0; i<100; i++)
  {
    cell1=cell2="";
    if(calc->gpaTable->item(i, 1)!=NULL)
      cell1=calc->gpaTable->item(i,1)->text().toStdString();
    if(calc->gpaTable->item(i, 2)!=NULL)
      cell2=calc->gpaTable->item(i,2)->text().toStdString();
    
    if(cell1 != "" && cell2 != "")
    {
      string valids=".0123456789";
      for(j=0; j<cell1.length(); j++)
      {
        if(string::npos==valids.find(cell1[j]))
        {
          output << "Invalid input in row " << i+1 
                 << ", column Credit Hours." << endl;
          calc->finalGpaOutput->setText(output.str().c_str());
          return;
        }
      }
        
      if(cell2.length()<1 || cell2.length()>2)
      {
        output << "Invalid input in row " << i+1
               << ", column Grade." << endl;
        calc->finalGpaOutput->setText(output.str().c_str());
        return;
      }
        
      if((cell2[0]<'A' || cell2[0]>'F') && (cell2[0]<'a' || cell2[0]>'f'))
      {
        output << "Invalid input in row " << i+1
               << ", column Grade." << endl;
        calc->finalGpaOutput->setText(output.str().c_str());
        return;
      }
        
      if(cell2.length()>=2)
      {
        if(cell2[1]!='+' && cell2[1]!='-')
        {
          output << "Invalid input in row " << i+1
                 << ", column Grade." << endl;
          calc->finalGpaOutput->setText(output.str().c_str());
          return;
        }
      }
        
      tmpGrade[0]=cell2[0];
      if(cell2.length()==2)
        tmpGrade[1]=cell2[1];
      else
        tmpGrade[1]='\0';
      
      input1=gradeToFloat(tmpGrade);
      input2=calc->gpaTable->item(i,1)->text().toFloat();
      accumulator+=input1*input2;
      numberOfCredits+=input2;
    }
    else if (cell1 != "" || cell2 != "")
    {
      output << "Invalid input on row " << i+1 << endl;
      calc->finalGpaOutput->setText(output.str().c_str());
      return;
    }
  }
  if(numberOfCredits!=0.0)
    result=accumulator/numberOfCredits;
  else
    result=0.0;
      
  tmp.setNum(result, 'g', 3);
  calc->finalGpaOutput->setText("GPA: " + tmp);
}






